﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace VirtualBook
{
    public partial class MainPage : UserControl
    {
        enum PageSettle
        {
            Previous,
            Next
        }

        private List<PageSet> m_pages = new List<PageSet>();

        private int Bookmark { get; set; }
        private bool IsMouseDown { get; set; }
        private double MouseDownOrigin { get; set; }
        private double CurrentHotspot { get; set; }
        private double OldHotspot { get; set; }

        private bool PageTurnInProgress { get; set; }
        private bool InterruptPending { get; set; }
        private PageSettle PageToSettleTo { get; set; }

        Storyboard Timer = new Storyboard();

        private Point LH0 { get; set; }
        private Point LH1 { get; set; }
        private Point LH2 { get; set; }
        private Point LH3 { get; set; }

        private Point RH0 { get; set; }
        private Point RH1 { get; set; }
        private Point RH2 { get; set; }
        private Point RH3 { get; set; }

        private const int RIGHT_MARGIN = 35;

        private const double DYNAMIC_SHADOW_OPACITY_BASE = 0.35;
        private const int AMT_PIXEL_PER_TICK = 4;

        public MainPage()
        {
            InitializeComponent();

            Bookmark = 0;
            IsMouseDown = false;
            MouseDownOrigin = 0.0;
            CurrentHotspot = LayoutRoot.Width - RIGHT_MARGIN;
            OldHotspot = CurrentHotspot;

            Timer.Completed += new EventHandler(OnTick);
            Timer.Duration = TimeSpan.FromMilliseconds(0);

            PageTurnInProgress = false;

            LH0 = new Point(0, 0);
            LH1 = new Point(0, 0);
            LH2 = new Point(0, 0);
            LH3 = new Point(0, 0);
            RH0 = new Point(0, 0);
            RH1 = new Point(0, 0);
            RH2 = new Point(0, 0);
            RH3 = new Point(0, 0);

            UpdateClippingAreas();
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            m_pages.Clear();
            m_pages.AddRange(PageLoader.GetPages());

            UpdateCurrentPage();

            PageID.Text = string.Format("Page {0} of {1}", Bookmark + 1, m_pages.Count);
        }

        private void UpdateCurrentPage()
        {
            RightPage.Background = m_pages[Bookmark].RHBrush;
            LeftPage.Background = m_pages[Bookmark].LHBrush;
        }

        private void SettleToNextPage()
        {
            //todo: animate this effect and set page turn to false

            PageToSettleTo = PageSettle.Next;
            StartAnimation();
        }

        private void SettleToPrevPage()
        {
            //todo: animate this effect and set pageturn to false

            PageToSettleTo = PageSettle.Previous;
            StartAnimation();
        }

        private void StopAnimation()
        {
            Timer.Stop();
        }

        private void StartAnimation()
        {
            Timer.Begin();
        }

        private void OnAnimationComplete()
        {
            if (PageTurnInProgress)
            {
                PageTurnInProgress = false;
                if (PageToSettleTo == PageSettle.Next)
                {
                    Bookmark++;
                    UpdateCurrentPage();
                    HideAllButBG();           
                }
                else
                {
                    HideAllButBG();
                }
            }

            PageID.Text = string.Format("Page {0} of {1}", Bookmark + 1, m_pages.Count);
        }

        private void OnTick(object sender, EventArgs e)
        {
            //if our animation is done, dont move any more
            if ((CurrentHotspot == (LayoutRoot.Width / 2)) || 
                (CurrentHotspot == LayoutRoot.Width - RIGHT_MARGIN))
            {
                Timer.Stop();
                OnAnimationComplete();
                return;
            }

            if (Timer.GetCurrentState() == ClockState.Stopped)
            {
                return;
            }

            if (CurrentHotspot < (LayoutRoot.Width - (2 * RIGHT_MARGIN)) * .75)
            {
                CurrentHotspot -= AMT_PIXEL_PER_TICK;
            }
            else
            {
                CurrentHotspot += AMT_PIXEL_PER_TICK;
            }

            //we dont want a negative hotspot or one that is greater than width
            CurrentHotspot = Math.Max((LayoutRoot.Width / 2), CurrentHotspot);
            CurrentHotspot = Math.Min(CurrentHotspot, LayoutRoot.Width - RIGHT_MARGIN);

            UpdateShownPages();

            Timer.Begin();
        }

        private void HideAllButBG()
        {
            //"zero" out the portion of the right page shown
            RH0 = new Point(this.Width - RIGHT_MARGIN, this.Height);
            RH1 = new Point(this.Width - RIGHT_MARGIN, this.Height);
            RH2 = new Point(this.Width - RIGHT_MARGIN, this.Height);
            RH3 = new Point(this.Width - RIGHT_MARGIN, this.Height);

            //"zero" out the portion of the underside of the left page shown
            LH0 = new Point(this.Width - RIGHT_MARGIN, this.Height);
            LH1 = new Point(this.Width - RIGHT_MARGIN, this.Height);
            LH2 = new Point(this.Width - RIGHT_MARGIN, this.Height);
            LH3 = new Point(this.Width - RIGHT_MARGIN, this.Height);

            UpdateClippingAreas();
        }

        private void UpdateClippingAreas()
        {
            LeftPageClip.Figures[0].StartPoint = LH0;
            ((LineSegment)LeftPageClip.Figures[0].Segments[0]).Point = LH1;
            ((LineSegment)LeftPageClip.Figures[0].Segments[1]).Point = LH2;
            ((LineSegment)LeftPageClip.Figures[0].Segments[2]).Point = LH3;

            RightPageClip.Figures[0].StartPoint = RH0;
            ((LineSegment)RightPageClip.Figures[0].Segments[0]).Point = RH1;
            ((LineSegment)RightPageClip.Figures[0].Segments[1]).Point = RH2;
            ((LineSegment)RightPageClip.Figures[0].Segments[2]).Point = RH3;
        }

        private void SetupPageFlip()
        {
            if (!PageTurnInProgress)
            {
                //set the respective images for the polygons
                LeftPageFlip.Background = m_pages[Bookmark + 1].LHBrush;
                RightPageFlip.Background = m_pages[Bookmark + 1].RHBrush;

                if (MouseDownOrigin <= (LayoutRoot.Width / 2))
                {
                    //set the bg image
                    UpdateCurrentPage();

                    //set the hotspot
                    CurrentHotspot = LayoutRoot.Width / 2;
                }
                else
                {
                    //set the hotspot
                    CurrentHotspot = LayoutRoot.Width - RIGHT_MARGIN;
                }

                UpdateShownPages();
            }
        }

        private void UpdateShownPages()
        {
            double height = WholePage.Height;
            double hPrime = WholePage.Width - (CurrentHotspot - RIGHT_MARGIN);
            double qWidth = WholePage.Width / 4;
            double a = 45 + ((45 * hPrime)/(qWidth * 2));
            double yPrime = hPrime * Math.Tan(a * (Math.PI / 180));
            double y = Math.Max(0, height - yPrime);
            double d = height / Math.Tan(a * (Math.PI / 180));
            double w = Math.Max(0, hPrime - d);

            RH0 = new Point((WholePage.Width / 2) - w, y);
            RH1 = new Point((WholePage.Width / 2), y);
            RH2 = new Point((WholePage.Width / 2), height);
            RH3 = new Point((WholePage.Width / 2) - hPrime, height);

            LH0 = new Point(0, y);
            LH1 = new Point(w, y);
            LH2 = new Point(hPrime, height);
            LH3 = new Point(0, height);

            UpdateClippingAreas();

            TranslateTransform tt = new TranslateTransform();
            tt.X = (CurrentHotspot - RIGHT_MARGIN) - hPrime;
            tt.Y = 0;

            RotateTransform rt = new RotateTransform();
            rt.CenterX = CurrentHotspot - RIGHT_MARGIN;
            rt.CenterY = height;
            rt.Angle = 180 - (2 * a);

            TransformGroup tg = new TransformGroup();
            tg.Children.Add(tt);
            tg.Children.Add(rt);

            LeftPageFlip.RenderTransform = tg;

            UpdateShadows(y, hPrime, w, a);
        }

        private void UpdateShadows(double y, double hPrime, double w, double a)
        {
            double shadowTopWidth = (Math.Sqrt(w) / Math.Sqrt(WholePage.Width / 2)) * 35;
            double shadowBottomWidth = (Math.Sqrt(hPrime) / Math.Sqrt(WholePage.Width / 2)) * 35;

            RShadowDynamic.Points[0] = new Point((WholePage.Width / 2) - w, y);
            RShadowDynamic.Points[1] = new Point(((WholePage.Width / 2) - w) + shadowTopWidth, y);
            RShadowDynamic.Points[2] = new Point(((WholePage.Width / 2) - hPrime) + shadowBottomWidth, WholePage.Height);
            RShadowDynamic.Points[3] = new Point((WholePage.Width / 2) - hPrime, WholePage.Height);

            double opaqueMultiplier = ((WholePage.Width / 2) - hPrime) / 20.0;
            opaqueMultiplier = Math.Min(opaqueMultiplier, 1);

            RShadowDynamicBrush.Opacity = DYNAMIC_SHADOW_OPACITY_BASE * opaqueMultiplier;

            double oldRot = 180 - (2 * a);
            double newOffset = hPrime - LShadowDynamic.Width;

            RotateTransform rt = new RotateTransform();
            rt.CenterX = hPrime;
            rt.CenterY = WholePage.Height;
            rt.Angle = 90 - oldRot - a;

            TranslateTransform tt = new TranslateTransform();
            tt.X = newOffset;
            tt.Y = -50;

            TransformGroup tg = new TransformGroup();
            tg.Children.Add(tt);
            tg.Children.Add(rt);

            LShadowDynamic.RenderTransform = tg;
        }

        private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            StopAnimation();

            if (PageTurnInProgress)
            {
                MouseDownOrigin = e.GetPosition(LayoutRoot).X;
                IsMouseDown = true;
            }
            else
            {
                //check if the click occured on the lhs or rhs
                if (e.GetPosition(LayoutRoot).X <= (LayoutRoot.Width / 2))
                {
                    //ignore click if it happened on the lhs and we're on the first page
                    if (Bookmark > 0)
                    {
                        //go ahead and change this, then we say that 
                        //we are always attempting to flip foward through pages
                        Bookmark--;
                        IsMouseDown = true;
                        MouseDownOrigin = e.GetPosition(LayoutRoot).X;
                    }
                }
                else
                {
                    //ignore click if it happened on the rhs and we're on the last page
                    if (Bookmark < m_pages.Count - 1)
                    {
                        IsMouseDown = true;
                        MouseDownOrigin = e.GetPosition(LayoutRoot).X;
                    }
                }
            }

            //setup the bg and the triangle at their starting positions
            if (IsMouseDown)
            {
                SetupPageFlip();
                OldHotspot = CurrentHotspot;
                PageTurnInProgress = true;
            }
        }

        private void UserControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (IsMouseDown)
            {
                double deltaX = e.GetPosition(LayoutRoot).X - MouseDownOrigin;

                /*//compute current hotspot
                if (MouseDownOrigin <= LayoutRoot.Width / 2)
                {
                    CurrentHotspot = (LayoutRoot.Width / 2) + (e.GetPosition(LayoutRoot).X - MouseDownOrigin);
                }
                else
                {
                    //hotspot is totalwidth - (change in x)
                    CurrentHotspot = LayoutRoot.Width - 
                        (MouseDownOrigin - e.GetPosition(LayoutRoot).X) - RIGHT_MARGIN;
                }*/

                CurrentHotspot = OldHotspot + deltaX;

                //we dont want a negative hotspot or one that is greater than width
                CurrentHotspot = Math.Max((LayoutRoot.Width / 2), CurrentHotspot);
                CurrentHotspot = Math.Min(CurrentHotspot, LayoutRoot.Width - RIGHT_MARGIN);

                UpdateShownPages();
            }
        }

        private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            OnMouseUp();
        }

        private void OnMouseUp()
        {
            if (IsMouseDown)
            {
                //check to see if page is past the halfway point when released
                if (CurrentHotspot < (LayoutRoot.Width - (2 * RIGHT_MARGIN)) * .75)
                {
                    SettleToNextPage();
                }
                else
                {
                    SettleToPrevPage();
                }

                IsMouseDown = false;
            }
        }

        private void WholePage_MouseLeave(object sender, MouseEventArgs e)
        {
            OnMouseUp();

        }
    }
}
